﻿using System.Collections.Generic;
using Ahresty.Entity;

namespace Ahresty.Algorithms
{
    /// <summary>
    /// 凸部の要素を操作するクラス
    /// </summary>
    internal class ProtuberanceHandler
    {
        #region Methods

        /// <summary>
        /// 指定の材料データから凸部になる要素を探し出す
        /// </summary>
        internal static Unit[] LookForProtuberances(IN3DData in3dData)
        {
            List<Unit> protuberances = new List<Unit>();

            for (short z = 0; z < in3dData.MaxZ; z++)
                for (short y = 0; y < in3dData.MaxY; y++)
                    for (short x = 0; x < in3dData.MaxX; x++)
                        if (ProtuberanceHandler.CheckProtuberance(in3dData, x, y, z))
                            protuberances.Add(new Unit { X = x, Y = y, Z = z, });

            return protuberances.ToArray();
        }

        /// <summary>
        /// 指定の材料データから凸部になる要素を探し出す
        /// </summary>
        internal static int LookForProtuberanceCount(IN3DData in3dData)
        {
            int result = 0;

            for (short z = 0; z < in3dData.MaxZ; z++)
                for (short y = 0; y < in3dData.MaxY; y++)
                    for (short x = 0; x < in3dData.MaxX; x++)
                        if (ProtuberanceHandler.CheckProtuberance(in3dData, x, y, z))
                            result++;

            return result;
        }

        /// <summary>
        /// 指定の要素が凸部かどうかをチェックする
        /// </summary>
        internal static bool CheckProtuberance(IN3DData in3dData, short x, short y, short z)
        {
            Unit protuberance = new Unit { X = x, Y = y, Z = z, };
            byte value = in3dData.Data[x, y, z];
            if (value != in3dData.MaterialID)
                return false;

            short maxX = in3dData.MaxX;
            short maxY = in3dData.MaxY;
            short maxZ = in3dData.MaxZ;

            byte[] surround = new byte[27];
            surround[0] = x == 0 || y == 0 || z == 0 ? (byte)' ' : in3dData.Data[x - 1, y - 1, z - 1];
            surround[1] = y == 0 || z == 0 ? (byte)' ' : in3dData.Data[x, y - 1, z - 1];
            surround[2] = x + 1 == maxX || y == 0 || z == 0 ? (byte)' ' : in3dData.Data[x + 1, y - 1, z - 1];
            surround[3] = x == 0 || z == 0 ? (byte)' ' : in3dData.Data[x - 1, y, z - 1];
            surround[4] = z == 0 ? (byte)' ' : in3dData.Data[x, y, z - 1];
            surround[5] = x + 1 == maxX || z == 0 ? (byte)' ' : in3dData.Data[x + 1, y, z - 1];
            surround[6] = x == 0 || y + 1 == maxY || z == 0 ? (byte)' ' : in3dData.Data[x - 1, y + 1, z - 1];
            surround[7] = y + 1 == maxY || z == 0 ? (byte)' ' : in3dData.Data[x, y + 1, z - 1];
            surround[8] = x + 1 == maxX || y + 1 == maxY || z == 0 ? (byte)' ' : in3dData.Data[x + 1, y + 1, z - 1];

            surround[9] = x == 0 || y == 0 ? (byte)' ' : in3dData.Data[x - 1, y - 1, z];
            surround[10] = y == 0 ? (byte)' ' : in3dData.Data[x, y - 1, z];
            surround[11] = x + 1 == maxX || y == 0 ? (byte)' ' : in3dData.Data[x + 1, y - 1, z];
            surround[12] = x == 0 ? (byte)' ' : in3dData.Data[x - 1, y, z];
            surround[13] = value;
            surround[14] = x + 1 == maxX ? (byte)' ' : in3dData.Data[x + 1, y, z];
            surround[15] = x == 0 || y + 1 == maxY ? (byte)' ' : in3dData.Data[x - 1, y + 1, z];
            surround[16] = y + 1 == maxY ? (byte)' ' : in3dData.Data[x, y + 1, z];
            surround[17] = x + 1 == maxX || y + 1 == maxY ? (byte)' ' : in3dData.Data[x + 1, y + 1, z];

            surround[18] = x == 0 || y == 0 || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x - 1, y - 1, z + 1];
            surround[19] = y == 0 || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x, y - 1, z + 1];
            surround[20] = x + 1 == maxX || y == 0 || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x + 1, y - 1, z + 1];
            surround[21] = x == 0 || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x - 1, y, z + 1];
            surround[22] = z + 1 == maxZ ? (byte)' ' : in3dData.Data[x, y, z + 1];
            surround[23] = x + 1 == maxX || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x + 1, y, z + 1];
            surround[24] = x == 0 || y + 1 == maxY || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x - 1, y + 1, z + 1];
            surround[25] = y + 1 == maxY || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x, y + 1, z + 1];
            surround[26] = x + 1 == maxX || y + 1 == maxY || z + 1 == maxZ ? (byte)' ' : in3dData.Data[x + 1, y + 1, z + 1];

            //XY & Z - 1
            if (value == surround[0] && value == surround[1] && value == surround[2] &&
                value == surround[3] && value == surround[4] && value == surround[5] &&
                value == surround[6] && value == surround[7] && value == surround[8] &&

                value != surround[9] && value != surround[10] && value != surround[11] &&
                value != surround[12] && value != surround[14] &&
                value != surround[15] && value != surround[16] && value != surround[17] &&

                value != surround[18] && value != surround[19] && value != surround[20] &&
                value != surround[21] && value != surround[22] && value != surround[23] &&
                value != surround[24] && value != surround[25] && value != surround[26])
                return true;

            //XY & Z + 1
            if (value != surround[0] && value != surround[1] && value != surround[2] &&
                value != surround[3] && value != surround[4] && value != surround[5] &&
                value != surround[6] && value != surround[7] && value != surround[8] &&

                value != surround[9] && value != surround[10] && value != surround[11] &&
                value != surround[12] && value != surround[14] &&
                value != surround[15] && value != surround[16] && value != surround[17] &&

                value == surround[18] && value == surround[19] && value == surround[20] &&
                value == surround[21] && value == surround[22] && value == surround[23] &&
                value == surround[24] && value == surround[25] && value == surround[26])
                return true;

            //XZ & Y - 1
            if (value == surround[0] && value == surround[1] && value == surround[2] &&
                value == surround[9] && value == surround[10] && value == surround[11] &&
                value == surround[18] && value == surround[19] && value == surround[20] &&

                value != surround[3] && value != surround[4] && value != surround[5] &&
                value != surround[12] && value != surround[14] &&
                value != surround[21] && value != surround[22] && value != surround[23] &&

                value != surround[6] && value != surround[7] && value != surround[8] &&
                value != surround[15] && value != surround[16] && value != surround[17] &&
                value != surround[24] && value != surround[25] && value != surround[26])
                return true;

            //XZ & Y + 1
            if (value != surround[0] && value != surround[1] && value != surround[2] &&
                value != surround[9] && value != surround[10] && value != surround[11] &&
                value != surround[18] && value != surround[19] && value != surround[20] &&

                value != surround[3] && value != surround[4] && value != surround[5] &&
                value != surround[12] && value != surround[14] &&
                value != surround[21] && value != surround[22] && value != surround[23] &&

                value == surround[6] && value == surround[7] && value == surround[8] &&
                value == surround[15] && value == surround[16] && value == surround[17] &&
                value == surround[24] && value == surround[25] && value == surround[26])
                return true;

            //YZ & X - 1
            if (value == surround[0] && value == surround[9] && value == surround[18] &&
                value == surround[3] && value == surround[12] && value == surround[21] &&
                value == surround[6] && value == surround[15] && value == surround[24] &&

                value != surround[1] && value != surround[10] && value != surround[19] &&
                value != surround[4] && value != surround[22] &&
                value != surround[7] && value != surround[16] && value != surround[25] &&

                value != surround[2] && value != surround[11] && value != surround[20] &&
                value != surround[5] && value != surround[14] && value != surround[23] &&
                value != surround[8] && value != surround[17] && value != surround[26])
                return true;

            //YZ & X + 1
            if (value != surround[0] && value != surround[9] && value != surround[18] &&
                value != surround[3] && value != surround[12] && value != surround[21] &&
                value != surround[6] && value != surround[15] && value != surround[24] &&

                value != surround[1] && value != surround[10] && value != surround[19] &&
                value != surround[4] && value != surround[22] &&
                value != surround[7] && value != surround[16] && value != surround[25] &&

                value == surround[2] && value == surround[11] && value == surround[20] &&
                value == surround[5] && value == surround[14] && value == surround[23] &&
                value == surround[8] && value == surround[17] && value == surround[26])
                return true;

            return false;
        }

        /// <summary>
        /// 指定の凸部要素を修正する
        /// </summary>
        /// <param name="in3dData">修正対象となる材料データ</param>
        /// <param name="protuberance">凸部要素</param>
        /// <param name="airMaterialID">空気材料番号</param>
        internal static void FixProtuberance(ref IN3DData in3dData, Unit protuberance, byte airMaterialID)
        {
            in3dData.Data[protuberance.X, protuberance.Y, protuberance.Z] = airMaterialID;
        }

        #endregion
    }
}